#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include <functional>

#include "Log.hpp"
extern Log lg;

// 枚举错误信息
enum 
{
    SOCKET_ERR=1,
    BIND_ERR,
    LISTEN_ERR
};

// 服务器默认端口号
uint16_t defaultport = 8081;

// 服务器默认ip
std::string defaultip = "0.0.0.0";

// 监听接口 listen 的第二个参数
// listen 函数的第二个参数 backlog 表示等待队列的最大长度。这个等待队列是用于存放那些已经到达但还没有被 accept 函数接受的连接请求。
// 当一个新的连接请求到达时，如果服务器的等待队列还没有满，那么这个连接请求就会被添加到队列中，等待服务器的 accept 函数来处理。
// 如果等待队列已经满了，那么新的连接请求可能就会被拒绝，客户端可能会收到一个 ECONNREFUSED 错误。
const int backlog = 10;

class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &p, TcpServer *t)
        :sockfd(fd), clientip(ip), clientport(p), tsvr(t)
    {}
public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer *tsvr;
};


class TcpServer
{
public:
    TcpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip) 
        :port_(port), ip_(ip)
    {}

    void InitServer()
    {
        // 创建套接字为 IPv4, 字节流(TCP)
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if(listensock_ < 0)
        {
            lg(Fatal, "create listensock error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
        lg(Info, "create listensock success, listensock: %d" , listensock_);
        
        // 初始化 local
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;   // 设置为 IPv4
        local.sin_port = htons(port_);// 保证端口号是网络字节序列
        // 将 IPv4 的字符串转化成 in_addr 并返回字符串的起始地址
        // 返回的字符串存储在静态区(多次调用只保存最后一次调用结果) 
        inet_aton(ip_.c_str(), &(local.sin_addr));

        // 当 local.sin_addr.s_addr 被设置为 INADDR_ANY 时
        // 意思是告诉操作系统，我们希望绑定的套接字监听所有可用的网络接口上的指定端口。
        // 这样设置后，当有数据包到达端口时，无论它们来自哪个网络接口，套接字都能接收到。
        // 在服务器编程中，这通常用于监听所有网络接口上的连接请求，而不是只监听某个特定的 IP 地址。
        // 这样，服务器可以接受来自任何网络接口的连接，而不仅仅是一个特定的接口。
        local.sin_addr.s_addr = INADDR_ANY;

        // 绑定套接字
        if(bind(listensock_, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            lg(Fatal,"bind error,errno: %d,errstring: %s" , errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind socket success, sockfd: %d" , listensock_);

        // tcp是面向连接的，服务器一般是比较“被动的”，服务器一直处于一种，一直在等待连接到来的状态
        // 监听套接字
        if (listen(listensock_, backlog) < 0)
        {
            lg(Fatal, "listen error, errno: %d, errstring: %s ", errno, strerror(errno));
            exit(LISTEN_ERR);
        }

        lg(Info, "listen socket success, sockfd: %d" , listensock_);
    }

    // 启动服务器
    void Start()
    {
        lg(Info, "TCP server is running...");
        for (;;)
        {
            //1．获取新连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            // accept 类似于 recvfrom 
            // 其返回一个文件描述符，后两个参数表示获取哪个用户的信息
            int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);

            // sockfd && listensock_
            // 举个简单的例子，对于一个农家乐来说会存在两种人
            // 一种是去拉客到农家乐内，另一种是在农家乐内进行服务的
            // listensock_ -> 拉客的人; sockfd -> 进行服务的人
            // listensock_ 只负责监听，如果监听失败会等待监听下一个主机
            // sockfd 只负责通信，其可能会变得越来越多
            if (sockfd < 0)
            {
                lg(Fatal, "listen erron,errno: %d, errstring: %s" , errno, strerror(errno));
                continue;
            }
            
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            // inet_ntop 将网络地址转换成文本表示形式
            // 它是 inet_aton 的逆函数，它将一个网络地址(通常是 IP 地址)从二进制形式转换为人类可读的字符串形式。
            // AF_INET 表示 IPv4 地址; &(client.sin_addr) 指向要转换的网络地址; clientip 是存储转换结果的字符串缓冲区; sizeof(clientip) 是 ipstr 缓冲区的大小
            // 确保 inet_ntop 不会写入超出缓冲区范围的内存。
            inet_ntop(AF_INET,&(client.sin_addr), clientip, sizeof(clientip));

            // 多线程版
            ThreadData *td = new ThreadData(sockfd, clientip, clientport);
            pthread_t tid;
            pthread_create(&tid, nullptr, Routine, td);
        }
    }

    static void *Routine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData*>(args);
        td->tsvr->Service(td->sockfd, td->clientip, td->clientport);
        delete td;
        return nullptr;
    }

    
    void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
    {
        char buffer[4096];
        while(true)
        {
            ssize_t n = read(sockfd, buffer, sizeof(buffer));
            if(n > 0)
            {
                buffer[n] = 0;
                std::cout << "client say: " << buffer << std::endl;
                std::string echo_string = "tcpserver echo: ";
                echo_string += buffer;

                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0)
            {
                lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
                break;
            }
            else 
            {
                lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
                break;
            }
        }
    }


    ~TcpServer()
    {
        if (listensock_ > 0) close(listensock_);
    }
private:
    int listensock_;   // 监听套接字
    std::string ip_;   // 服务器ip
    uint16_t port_;    // 服务器进程的端口号
    bool isrunning_;   // 服务器运行状态
};
